/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2019-2019. All rights reserved.
 * Description: unittest
 */
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <sys/time.h>
#include <string.h>
#include <errno.h>  
#include <limits.h>
#include <unistd.h>

#include "va_encode_api.h"

#define ENC_STATUS_CHECK_FAIL_IF(encStatus, func)                         \
    if ((encStatus) != ENC_STATUS_SUCCESS) {                              \
        printf("%s failed\n", func);                                      \
        return;                                                           \
    }                                                                     \

#define BIT_RATE_1 (2*1000*1000)
#define TEST_YUV_FILE "/data/720.yuv"
#define ARRAY_SIZE 256
#define FRAME_RATE 30
#define WIDTH 720
#define HEIGHT 1280
#define GOP_SIZE 60

void EncTurboBo()
{
    ENCStatus encStatus;
    avcenc_codedbuf_s codedBuffer;
    avcencode_config_s config; 
    avcencode_input_s input;
    unsigned long *boHandle = NULL;

    config.width_input = WIDTH;
    config.height_input = HEIGHT; 
    config.width_out = WIDTH; // 32*n, 32bit align for some display
    config.height_out = HEIGHT; 
    config.rate_control = ENC_RC_VBR;
    config.bit_rate = BIT_RATE_1;
    config.frame_rate = FRAME_RATE;
    config.gop_size = GOP_SIZE;
    config.profile_idc = ENC_PROFILE_IDC_BASELINE;
    
    input.source_raw_type = SOURCE_RAW_BO;
    input.output_data_type = OUTPUT_H264;
    
    void *ctxHandle = EncTurboCreate(config, input);
    encStatus = EncTurboStart(ctxHandle);
    ENC_STATUS_CHECK_FAIL_IF(encStatus, "EncTurboStart");

    int totalFrames = 100;
    for (int frameIndex = 0; frameIndex < totalFrames; frameIndex++) {
        encStatus = EncTurboEncodeOneFrame(ctxHandle, boHandle, frameIndex, &codedBuffer);
        ENC_STATUS_CHECK_FAIL_IF(encStatus, "EncTurboEncodeOneFrame");
        if (input.output_data_type == OUTPUT_H264) {
            printf("the output data is h264,size=%d, data is %p\n", codedBuffer.coded_mem_size, codedBuffer.coded_mem);
        } else if (input.output_data_type == OUTPUT_YUV) {
            printf("the output data is YUV,size=%d, data is %p\n", codedBuffer.coded_mem_size, codedBuffer.coded_mem);
        }
        
        if (frameIndex == totalFrames - 1) {
            EncTurboUpdateEncodeParam(ctxHandle, config);
        }
    }
    
    encStatus = EncTurboStop(ctxHandle);
    ENC_STATUS_CHECK_FAIL_IF(encStatus, "EncTurboStop");

    encStatus = EncTurboDestory(ctxHandle);
    ENC_STATUS_CHECK_FAIL_IF(encStatus, "EncTurboDestory");
    return;
}

long CalcFrameNum(FILE *inputFp, int frameSize)
{
    if (!inputFp || frameSize <= 0) {
        return 0;
    }
    
    fseeko(inputFp, (off_t)0, SEEK_END);
    long fileSize = ftello(inputFp);
    fseeko(inputFp, (off_t)0, SEEK_SET);
    if ((fileSize < frameSize) || (fileSize % frameSize)) {
        printf("The YUV file's size is not correct\n");
        return 0;
    }
    int frameTotalNum = fileSize / frameSize;
    return frameTotalNum;
}

void EncTurbo(avcencode_config_s config, avcencode_input_s input, FILE *inputFp, unsigned char *sourceDataBuffer,
              bool recycle)
{
    avcenc_codedbuf_s codedBuffer;
    void *ctxHandle;
    ENCStatus encStatus;
    int frameSize;
    long frameTotalNum;

    frameSize = config.width_input * config.height_input + ((config.width_input * config.height_input) >> 1);
    frameTotalNum = CalcFrameNum(inputFp, frameSize);

    ctxHandle = EncTurboCreate(config, input);
    encStatus = EncTurboStart(ctxHandle);
    ENC_STATUS_CHECK_FAIL_IF(encStatus, "EncTurboStart");
 
    for (int frameIndex = 0; frameIndex < frameTotalNum; frameIndex++) {
        // read source data
        int seekOffset = frameSize * frameIndex;
        fseeko(inputFp, seekOffset, SEEK_SET);
        fread(sourceDataBuffer, 1, frameSize, inputFp);

        encStatus = EncTurboEncodeOneFrame(ctxHandle, (unsigned long*)sourceDataBuffer, frameIndex, &codedBuffer);
        ENC_STATUS_CHECK_FAIL_IF(encStatus, "EncTurboEncodeOneFrame");
        
        if (recycle) {
            frameIndex = (frameIndex == frameTotalNum - 1) ? 0 : frameIndex;
        }

        // here process codedBuffer
    }
    
    encStatus = EncTurboStop(ctxHandle);
    ENC_STATUS_CHECK_FAIL_IF(encStatus, "EncTurboStop");

    encStatus = EncTurboDestory(ctxHandle);
    ENC_STATUS_CHECK_FAIL_IF(encStatus, "EncTurboDestory");
    return;
}

void InputParamDefault(avcencode_config_s *config, avcencode_input_s *input)
{
    // init 
    config->width_input = WIDTH;
    config->height_input = HEIGHT;
    config->width_out = config->width_input;
    config->height_out = config->height_input;
    config->bit_rate = BIT_RATE_1;
    config->frame_rate = FRAME_RATE;
    config->gop_size = GOP_SIZE;
    config->rate_control = ENC_RC_VBR;
    config->profile_idc = ENC_PROFILE_IDC_BASELINE;
    config->drm_device_path = DEV_DRI_RENDERD128;
    input->source_raw_type = SOURCE_RAW_YUV_FILE;
}

int main(int argc, char *argv[])
{
    printf("start to test, argc=%d,argv=%p\n", argc, argv);

    FILE *inputFp;
    unsigned char *sourceDataBuffer = NULL;
    avcencode_config_s config; 
    avcencode_input_s input;
    char inputFile[ARRAY_SIZE];
    if (strlen(TEST_YUV_FILE) > ARRAY_SIZE) {
        return 0;
    }

    strcpy(inputFile, TEST_YUV_FILE);
    InputParamDefault(&config, &input);
    int frameSize = config.width_input * config.height_input + ((config.width_input * config.height_input) >> 1);

    inputFp = fopen(inputFile, "rb");
    if (inputFp == NULL) {
        printf("Can't open input YUV file\n");
        goto END;
    }

    sourceDataBuffer = (unsigned char *)malloc(frameSize);
    if (!sourceDataBuffer) {
        goto END;
    }
    EncTurbo(config, input, inputFp, sourceDataBuffer, false);

END:
    if (inputFp) {
        fclose(inputFp);
        inputFp = NULL;
    }
    if (sourceDataBuffer) {
        free(sourceDataBuffer);
        sourceDataBuffer = NULL;
    }
    printf("Test quit\n");
    return 1;
}
